package com.w.concurrent.executor.selfdefined;

import lombok.Getter;
import lombok.Setter;
import org.aspectj.weaver.ast.Test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;


@Setter
@Getter
public class TestIO {

    private static String sys = System.getProperty("line.separator");

    private static final String IOFILENAME = "E:" + File.separator + "Test" + File.separator + "test.txt";

    private static final String NIOFILENAME = "E:" + File.separator + "Test" + File.separator + "testNIO.txt";

    static {
        System.out.println(sys);
        System.out.println(IOFILENAME);
        System.out.println(NIOFILENAME);
    }

    public static void writeFileByIO(List<String> iStrList, BufferedWriter bufferedWriter, File file) {
        try {
            for (String str : iStrList) {
                bufferedWriter.write(str + sys);
            }
            bufferedWriter.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void writeFileByNIO(List<String> iStrList, FileChannel fileChannel) {

        try {
            StringBuffer stringBuffer = new StringBuffer();
            for (int i = 0; i < iStrList.size(); i++) {
                stringBuffer.append(iStrList.get(i) + sys);
            }
            ByteBuffer bbf = ByteBuffer.wrap(stringBuffer.toString().getBytes());
            bbf.put(stringBuffer.toString().getBytes());
            bbf.flip();
            fileChannel.write(bbf);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws IOException {

//        List<List<String>> stringList = new ArrayList<>();
//
//        for (int i = 0; i < 100; i++) {
//            List<String> tmp = new ArrayList<>();
//            for (int j = 0; j < 1000; j++) {
//                StringBuilder stringBuilder = new StringBuilder(i + j);
//                for (int k = 0; k < 1000; k++) {
//                    stringBuilder.append(k);
//                }
//                tmp.add(stringBuilder.toString());
//            }
//            stringList.add(tmp);
//        }
//
//        long ioStartTime = System.currentTimeMillis();
//        File file = new File(IOFILENAME);
//        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(file, true));
//
//        for (int i = 0; i < stringList.size(); i++) {
//            List<String> tmp = stringList.get(i);
//            writeFileByIO(tmp, bufferedWriter, file);
//        }
//        System.out.println((System.currentTimeMillis() - ioStartTime) / 1000);
//
//
//        long nioStartTime = System.currentTimeMillis();
//        File file2 = new File(NIOFILENAME);
//        FileOutputStream outputStream;
//        FileChannel fileChannel;
//        outputStream = new FileOutputStream(file2);
//        fileChannel = outputStream.getChannel();
//
//        for (int i = 0; i < stringList.size(); i++) {
//            List<String> tmp = stringList.get(i);
//            writeFileByNIO(tmp, fileChannel);
//            outputStream.flush();
//        }
//
//        outputStream.close();
//        fileChannel.close();
//        System.out.println((System.currentTimeMillis() - nioStartTime) / 1000);

//        int bufSize = 100;
//        File fin = new File("C:\\Users\\za-wangyiwen\\Desktop\\CP0007_accounting_to_adapter_20181027.txt");
//        ByteBuffer rBuffer = ByteBuffer.allocate(bufSize);
//
//        FileChannel fcin = new RandomAccessFile(fin, "r").getChannel();
//        long startTime = System.currentTimeMillis();
//        List<String> result = readLine();
//        System.out.println((System.currentTimeMillis() - startTime) / 1000);
//        System.out.println(result.size());

    }


    public static List<String> readFileByLine(int bufSize, FileChannel fcin, ByteBuffer rBuffer) {
        String enterStr = "\n";
        List<String> result = new ArrayList<>();
        try {
            byte[] bs = new byte[bufSize];

            int size = 0;
            StringBuffer strBuf = new StringBuffer("");
            while (fcin.read(rBuffer) != -1) {
                int rSize = rBuffer.position();
                rBuffer.rewind();
                rBuffer.get(bs);
                rBuffer.clear();
                String tempString = new String(bs, 0, rSize);

                int fromIndex = 0;
                int endIndex = 0;
                while ((endIndex = tempString.indexOf(enterStr, fromIndex)) != -1) {
                    String line = tempString.substring(fromIndex, endIndex);
                    line = new String(strBuf.toString() + line);

                    result.add(line);
                    strBuf.delete(0, strBuf.length());
                    fromIndex = endIndex + 1;

                }
                if (rSize > tempString.length()) {
                    strBuf.append(tempString.substring(fromIndex, tempString.length()));
                } else {
                    strBuf.append(tempString.substring(fromIndex, rSize));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }


//    private FileChannel fileChannel;
//    private MappedByteBuffer mappedByteBuffer;
//    private RandomAccessFile randomAccessFile;
//    private File file;
//    private int readIndex;
//
//    private int nextReadInex(){
//        readIndex ++;
//        return readIndex;
//    }

//    private  void getNIOReader(File file) throws IOException{
//        if(this.file == null){
//            this.file = file;
//            this.randomAccessFile = new RandomAccessFile(file, "rw");
//            this.fileChannel = randomAccessFile.getChannel();
//            this.mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, BigDecimal.ZERO.intValue(), this.randomAccessFile.length());
//            long read = fileChannel.read(mappedByteBuffer);
//            if(read != -1){
//                mappedByteBuffer.flip();
//            }
//        }
//    }

//
//    private  List<String> readLineByPart(TestIO io,File file) throws IOException {
//
//        getNIOReader(file);
//
//        MappedByteBuffer mappedByteBuffer = io.getMappedByteBuffer();
//        ByteBuffer stringBuffer = ByteBuffer.allocate(1024);
//        List<String> result = new ArrayList<>();
//
//        return result;
//    }

//    private void getVal(List<String> result, TestIO io){
//        int readIndex = io.getReadIndex();
//        MappedByteBuffer mappedByteBuffer = io.getMappedByteBuffer();
//
//        ByteBuffer stringBuffer = ByteBuffer.allocate(1024);
//        while (mappedByteBuffer.hasRemaining()){
//            byte b = mappedByteBuffer.get();
//            // 换行或回车
//            if (b == 10 || b == 13) {
//                stringBuffer.flip();
//                // 这里就是一个行
//                String line = null;
//                // 解码已经读到的一行所对应的字节
//                line = Charset.forName("utf-8").decode(stringBuffer).toString();
//                System.out.printf("readInex:{%d},mappedByteBuffer.Posion:{%d},mappedByteBuffer.Limit:{%d}",readIndex,mappedByteBuffer.position(),mappedByteBuffer.limit());
//                io.nextReadInex();
//                stringBuffer.clear();
//            } else {
//                if (stringBuffer.hasRemaining()) {
//                    stringBuffer.put(b);
//                }else { // 空间不够扩容
//                    stringBuffer = reAllocate(stringBuffer);
//                    stringBuffer.put(b);
//                }
//            }
//        }
//    }


    /**
     * 1. 在测试中因为需要分配空间出现堆溢出的情况，记住出现问题的根本原因是什么才好分析
     */
    private static List<String> readLine() throws IOException {
//        File file = new File("C:\\Users\\za-wangyiwen\\Desktop\\CP0007_accounting_to_adapter_20181028.txt");
        File file = new File("C:\\Users\\admin\\Desktop\\CP0007_accounting_to_adapter_20181027.txt");
        RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
        FileChannel channel = randomAccessFile.getChannel();
        // 文件总大小
        long size = file.length();
        //获取读、写之和所占用虚拟内存 倍数
        long copycount = size / Integer.MAX_VALUE;
        // 根据倍数确认分割份数
        int copynum = copycount >= 1 ? (int) copycount + 2 : (int) copycount + 2;
        //每块分割大小<每次读写的大小>
        long countSize = size / copynum;
        //初始读、写大小
        long lontemp = countSize;

        MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, BigDecimal.ZERO.intValue(), lontemp);
        long bytesRead = channel.read(buffer);
        ByteBuffer stringBuffer = ByteBuffer.allocate(1024);
        List<String> result = new ArrayList<>();
        int i = 0;
        int count = 0;
        boolean shutDown = false;
        while (bytesRead != -1) {
            //之前是写buffer，现在要读buffer
            buffer.flip();// 切换模式，写->读
            while (buffer.hasRemaining()) {
                byte b = buffer.get();
                // 换行或回车
                if (b == 10 || b == 13) {
                    count++;
                    stringBuffer.flip();
                    // 这里就是一个行
                    String line = null;
                    // 解码已经读到的一行所对应的字节
                    line = Charset.forName("utf-8").decode(stringBuffer).toString();
                    System.out.println("第： " + count);
                    System.out.println(line);
                    // 产生OOM的原因之一
//                    if(result.size() < 1000) {
//                        result.add(line);
//                    } else {
//                        shutDown = true;
//                        break;
//                    }
                    stringBuffer.clear();
                } else {
                    if (stringBuffer.hasRemaining()) {
                        stringBuffer.put(b);
                    } else { // 空间不够扩容
                        stringBuffer = reAllocate(stringBuffer);
                        stringBuffer.put(b);
                    }
                }
            }
//            if(shutDown){
//                break;
//            }
//            System.gc();                //手动调用 GC       <必须的，否则出现异常>
//            System.runFinalization();   //运行处于挂起终止状态的所有对象的终止方法。<必须的，否则出现异常>

            i += lontemp;
            // 剩余字节
            lontemp = size - i;
            //buffer.clear();// 清空,position位置为0，limit=capacity
            if (lontemp > 0) {
                //如果剩余字节 大于 每次分割字节 则 读取 每次分割字节 ，否则 读取剩余字节
                lontemp = lontemp > countSize ? countSize : lontemp;
                buffer = channel.map(FileChannel.MapMode.READ_WRITE, BigDecimal.ZERO.intValue(), lontemp);
//            buffer.clear();
                //  继续往buffer中写
                bytesRead = channel.read(buffer);
            } else {
                buffer.clear();
                //  继续往buffer中写
                bytesRead = channel.read(buffer);
            }
        }
        randomAccessFile.close();
        return result;
    }

    private static ByteBuffer reAllocate(ByteBuffer stringBuffer) {
        final int capacity = stringBuffer.capacity();
        byte[] newBuffer = new byte[capacity * 2];
        System.arraycopy(stringBuffer.array(), 0, newBuffer, 0, capacity);
        return (ByteBuffer) ByteBuffer.wrap(newBuffer).position(capacity);
    }

    private static void printMemory() {
        System.out.println("----------------------------------------------------");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("|   maxMemory(最大可用内存):  + " + Runtime.getRuntime().maxMemory() + "   |");
        System.out.println("|   freeMemory(当前JVM空闲内存): " + Runtime.getRuntime().freeMemory() + ") |");
        System.out.println("|   totalMemory: " + Runtime.getRuntime().totalMemory() + "  |");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("|                                                   |");
        System.out.println("----------------------------------------------------");
    }
}
